package com.broada.apm.template;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.elasticsearch.client.Client;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

import com.broada.apm.es.indices.service.impl.IndexServiceImpl;
import com.broada.apm.protocol.ZeroClient;
import com.broada.apm.utils.EsClientUtil;


/**
 * mock对象注入
 * @author jianfei.xu
 * @date   2016年4月7日
 *
 */
public class MockitoDependencyInjectionTestExecutionListener extends DependencyInjectionTestExecutionListener {
    private Map<String, MockObject> mockObject   = new HashMap<String, MockObject>();
    private List<Field>             injectFields = new LinkedList<Field>();
    
    @Override
    protected void injectDependencies(final TestContext testContext) throws Exception {
    	/*
    	 * mock client对象
    	 */
    	String esPath = System.getProperty("es.config.path");
    	String apmPath = System.getProperty("apm.config.file");
    	try {
    		System.setProperty("es.config.path", "/src/test/resource/conf");
    		System.setProperty("apm.config.file", "/src/test/resource/conf/configure.properties");
			Client client = PowerMockito.mock(Client.class);
			PowerMockito.mockStatic(EsClientUtil.class);
			PowerMockito.when(EsClientUtil.getTransportClient()).thenReturn(client);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(null != esPath) System.setProperty("es.config.path", esPath);
			if(null != apmPath) System.setProperty("apm.config.file", apmPath);
		}
    	ZeroClient zeroClient = PowerMockito.mock(ZeroClient.class);
    	PowerMockito.whenNew(ZeroClient.class).withNoArguments().thenReturn(zeroClient);
    	
    	try {
    		IndexServiceImpl indexService = PowerMockito.mock(IndexServiceImpl.class);
    		PowerMockito.whenNew(IndexServiceImpl.class).withNoArguments().thenReturn(indexService);
			PowerMockito.when(indexService.searchIndex(Mockito.anyVararg())).thenReturn(new String[]{});
		} catch (Exception e) {
		}
    	
        super.injectDependencies(testContext);
        init(testContext);
    }
    
    private void init(final TestContext testContext) throws Exception {
        Object bean = testContext.getTestInstance();
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation antt : annotations) {
                if (antt instanceof org.mockito.Mock) {//注入mock实例
                    MockObject obj = new MockObject();
                    obj.setType(field.getType());
                    obj.setObj(PowerMockito.mock(field.getType()));
                    field.setAccessible(true);
                    field.set(bean, obj.getObj());
                    mockObject.put(field.getName(), obj);
                } else if (antt instanceof SpyExt) {//注入spy实例
                    MockObject obj = new MockObject();
                    obj.setType(field.getType());
                    field.setAccessible(true);
                    obj.setObj(PowerMockito.spy(ProxyTargetUtils.getTarget(field.get(bean))));
                    field.set(bean, obj.getObj());
                    mockObject.put(field.getName(), obj);
                } else if (antt instanceof Autowired) {//只对autowire重新注入
                    injectFields.add(field);
                }
            }
        }
        
        for (Field field : injectFields) {
            field.setAccessible(true);
            Object object = ProxyTargetUtils.getTarget(field.get(bean));
            Field[] targetFields = object.getClass().getFields();
            
            MockObject mockObj = null;
            for (int i = 0; i < targetFields.length; i++) {//针对每个需要重新注入的字段
            	mockObj = mockObject.get(targetFields[i].getName());
            	if(null != mockObj) {
            		targetFields[i].setAccessible(true);
                    targetFields[i].set(object, mockObj.getObj());
            	}
            	Object object1 = ProxyTargetUtils.getTarget(targetFields[i].get(object));
            	Field[] targetFields1 = object1.getClass().getFields();
            	MockObject mockObj1 = null;
            	for (int j = 0; j < targetFields1.length; j++) {
                	mockObj1 = mockObject.get(targetFields1[j].getName());
                	if(null != mockObj1) {
                		targetFields1[j].setAccessible(true);
                        targetFields1[j].set(object1, mockObj1.getObj());
                	}
            	}
            	
            }
        }
        
    }
    
    private static class MockObject {
        private Object   obj;
        private Class<?> type;

        public MockObject(){
        }

        public Object getObj() {
            return obj;
        }

        public void setObj(Object obj) {
            this.obj = obj;
        }

        public Class<?> getType() {
            return type;
        }

        public void setType(Class<?> type) {
            this.type = type;
        }
    }
    
}
